package com.GB26875_3_2011.server;

import com.GB26875_3_2011.codec.MessageDecoder;
import com.GB26875_3_2011.codec.MessageEncoder;
import com.GB26875_3_2011.codec.StartAndEndSymbolsCodec;
import com.GB26875_3_2011.config.GB26875Properties;
import com.GB26875_3_2011.message.MessageSimpleHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.extern.slf4j.Slf4j;

/**
 * @author 蒋仕国
 * @projectName GB26875_3_211
 * @ClassName TCPServer
 * @date 2025-01-13 13:04
 * @Description: TODO []
 */
@Slf4j
public class TCPServer implements Server{

    private final GB26875Properties gB26875Properties;
    private final MessageEncoder messageEncoder;
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final EventExecutorGroup businessGroup;
    private final MessageSimpleHandler messageSimpleHandler;

    public TCPServer(GB26875Properties gB26875Properties,
                     MessageEncoder messageEncoder,
                     EventLoopGroup bossGroup,
                     EventLoopGroup workerGroup,
                     EventExecutorGroup businessGroup,
                     MessageSimpleHandler messageSimpleHandler) {
        this.gB26875Properties = gB26875Properties;
        this.messageEncoder = messageEncoder;
        this.bossGroup = bossGroup;
        this.workerGroup = workerGroup;
        this.businessGroup = businessGroup;
        this.messageSimpleHandler = messageSimpleHandler;
    }

    @Override
    public void start() throws Exception {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(isLinux() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                .option(ChannelOption.SO_BACKLOG, gB26875Properties.getBacklog())
                // 日志登记
                .handler(new LoggingHandler(gB26875Properties.getLogLevel()))
                .childOption(ChannelOption.SO_KEEPALIVE, gB26875Properties.isKeepAlive())
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    /**
                     * This method will be called once the {@link Channel} was registered. After the method returns this instance
                     * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
                     * @param ch the {@link Channel} which was registered.
                     * @throws Exception is thrown if an error occurs. In that case it will be handled by
                     * {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
                     * the {@link Channel}.
                     */
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        //设置心跳检测
                        pipeline.addLast(new IdleStateHandler(gB26875Properties.getReadTimeout(),
                                gB26875Properties.getWriteTimeout(),
                                gB26875Properties.getAllTimeout(), gB26875Properties.getTimeoutUnit()));
                        // 添加报头尾去除/添加器
                        pipeline.addLast(new StartAndEndSymbolsCodec(gB26875Properties.getStartFlag(),gB26875Properties.getEndFlag()));
                        // 添加解码器
                        pipeline.addLast(new MessageDecoder());
                        // 添加编码器
                        pipeline.addLast(messageEncoder);
                        // 添加业务处理
                        pipeline.addLast(businessGroup, messageSimpleHandler);
                    }
                });
        //内存泄漏检测
        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
        ChannelFuture channelFuture = serverBootstrap.bind(gB26875Properties.getServerPort()).sync().addListener(future -> {
            if (future.isSuccess()) {
                log.info("GB26875协议服务启动完毕, port={}", gB26875Properties.getServerPort());
            }
        });
        channelFuture.channel().closeFuture().addListener(future -> {
            log.info("GB26875 协议服务停止, port={}", gB26875Properties.getServerPort());
            bossGroup.shutdownGracefully().syncUninterruptibly();
            workerGroup.shutdownGracefully().syncUninterruptibly();
            businessGroup.shutdownGracefully().syncUninterruptibly();
        });
    }
}
